package com.gking.resourcePool.AOP;

import com.gking.resourcePool.AOP.annotationUtil.rateLimit.RateLimit;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;


@Slf4j
@Aspect
@Component
public class RateLimitAspect {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private final HttpServletRequest request;

    public RateLimitAspect(HttpServletRequest request) {
        this.request = request;
    }


    private String getClientIP() {
        String remoteAddr = "";

        if (request != null) {
            remoteAddr = request.getHeader("X-Forwarded-For");
            if (remoteAddr == null || remoteAddr.isEmpty() || "unknown".equalsIgnoreCase(remoteAddr)) {
                remoteAddr = request.getHeader("X-Real-IP");
            }
            if (remoteAddr == null || remoteAddr.isEmpty() || "unknown".equalsIgnoreCase(remoteAddr)) {
                remoteAddr = request.getRemoteAddr();
            }
        }

        return remoteAddr;
    }

    private boolean accessStatus(String key, long time) {
        String s = stringRedisTemplate.opsForValue().get(key);

        if (s == null) {
            stringRedisTemplate.opsForValue().set(key, "1", time, TimeUnit.MILLISECONDS);
            return true;
        }
        return false;
    }


    @Around("@annotation(rateLimit)")
    public Object limitFrequency(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
        String ip = getClientIP();
        String methodName = joinPoint.getSignature().toShortString();
        String key = methodName + ":" + ip;

        boolean b = accessStatus(key, rateLimit.time());

        if (b) {
            // 执行目标方法
            return joinPoint.proceed();
        }

        // 忽略请求，返回null或默认值
        return null; // 或者返回 ""、false、一个空对象等
    }

}
